import tzlocal, threading, os
import time as T
from support.network.push import MediaPush
from support.files.media import MediaFile
from support.utils.toolkit import ToolKit
from support.utils.toolkit import Log
from data.zhihu import ZHIHUHOT
from data.embed import EMBED 
from data.movie import Movie
from data.quote import QUOTE
from support.dbms.helper import DBMS
from support.utils.constants import Constants
from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
from apscheduler.executors.pool import ThreadPoolExecutor

class FilmSchedule:
    __instance = None

    def __new__(cls, *args, **kwargs):
        if not cls.__instance:
            cls.__instance = super().__new__(cls, *args, **kwargs)
        return cls.__instance

    def __init__(self):
        try:
            len(self.playlist.keys())
        except:
            self.running = False
            self.playlist = {}
            self.mediadir = None
            self.info = {'open':Constants.DEFAULT_OPEN_TIME, 'close':Constants.DEFAULT_CLOSE_TIME, 'wait':0}

    def setMediaDir(self, mediadir):
        self.mediadir = mediadir
        return self

    def setBusinessTime(self, *, open=None, close=None):
        if(open):self.info['open'] = open
        if(close):self.info['close'] = close
        return self

    def run(mediadir):
        FilmSchedule().setMediaDir(mediadir).start()

    def start(self):
        self.running = True
        threading.Timer(0, self.play).start()

    def stop(self):
        self.running = False

    def play(self):
        while(self.running):
            openDate = ToolKit.getSpecialTime(hour=self.info['open'])
            closeDate = ToolKit.getSpecialTime(hour=self.info['close'], limitToday=True)
            if((openDate < closeDate) or (ToolKit.getTimeNow() > closeDate)):
                interval = ToolKit.calcIntervalSeconds(openDate)
                if(interval > 5 * 60):
                    self.info['wait'] = interval - 5 * 60
                    T.sleep(interval - 5 * 60)
            self.info['wait'] = 0
            self.playlist = self.__createNewPlayList__()
            orderlist = []
            for id in self.playlist.keys():
                film = self.playlist[id]
                for time in film['timetable']:
                    orderlist.append([id, film['path'], int(time.hour * 60 + time.minute)])
            orderlist.sort(key=lambda x:x[2])
            openDate = ToolKit.getSpecialTime(hour=self.info['open'], limitToday=True)
            interval = ToolKit.calcIntervalSeconds(openDate) - 5 * 60
            if(interval > 0):T.sleep(interval)
            for film in orderlist:
                now = ToolKit.getTimeNow()
                nowtime = now.hour * 60 + now.minute
                if(film[2] >= nowtime):
                    interval = (film[2] - nowtime) * 60
                    self.info['wait'] = interval
                    T.sleep(interval)
                    self.info['wait'] = 0
                    self.info['film'] = film[0]
                    MediaPush.push(film[1])
                self.playingMovie = film[0]
            if(not self.running):break

    def __createNewPlayList__(self):
        playlist = {}
        start = ToolKit.getSpecialTime(hour=self.info['open'], limitToday=True)
        end  = ToolKit.getSpecialTime(hour=self.info['close'], limitToday=True)
        cur  = start
        filemap = MediaFile().queryMediaFiles(self.mediadir)
        while(cur < end):
            for id in filemap.keys():
                if(id in playlist):
                    program = playlist[id]['timetable']
                    program.append(cur)
                else:
                    playlist[id] = {'name':filemap[id]['name'], 'path':filemap[id]['path'], 'duration':filemap[id]['duration'], 'timetable':[cur]}
                    movieDriver = Movie("http://{}:4444".format(os.environ.get("SELENIUM_HUB_HOST")))
                    try:
                        playlist[id]["info"] = movieDriver.query(filemap[id]['name']).copy()
                    except Exception as e:
                        playlist[id]["info"] = []
                        playlist[id]["info"].append(self.__loadDefaultFilmInfo__())
                        Log.e(e)
                    movieDriver.exit()
                cur = ToolKit.calcDeltaTime(cur, seconds=int(filemap[id]['duration'] +60))
                if(cur > end):break
        return playlist

    def __loadDefaultFilmInfo__(self):
        infomap = {}
        infomap["title"] = "No valid data found"
        infomap["rating"] = 0
        infomap["meta"] = "No valid data found"
        infomap["meta2"] = "No valid data found" 
        infomap["cover"] = "https://arts.ray0728.cn/images/2024/11/25/default_film.jpg"
        return infomap
   

    def __transformTime__(self, seconds):
        minutes = int(seconds / 60)
        seconds -= minutes * 60
        hours = int(minutes/60)
        if(hours > 0):minutes -= (hours * 60)
        return "{:0>2d}:{:0>2d}:{:0>2d}".format(hours, minutes, int(seconds))

    def getPlayLst(self):
        lst = []
        for id in self.playlist.keys():
            file = self.playlist[id]
            info = {'id':id, 'name':file['name'], 'duration':self.__transformTime__(file['duration'])}
            info['timetable'] = [tt.strftime("%H:%M") for tt in file['timetable']]
            if("info" in file.keys()):
                info["name"] = file["info"][0]["title"]
                info["rating"] = file["info"][0]["rating"]
                info["meta"] = file["info"][0]["meta"]
                info["meta2"] = file["info"][0]["meta2"]
                info["cover"] = file["info"][0]["cover"]
            lst.append(info)
        return lst

    def getSnapshot(self, id, num):
        lst = []
        if(id in self.playlist.keys()):
            if('snapshot' not in self.playlist[id]):self.playlist[id]['snapshot'] = MediaFile.snapshot(self.playlist[id]['path'], 7, self.playlist[id]['duration'])
            snapshot = self.playlist[id]['snapshot']
            step = 1
            index = 0
            if(num == 4):step = 2
            if(num == 3):step = 3
            if(num == 2):step = 6
            if(num == 1):index = 3
            lst = [snapshot[index + step * i] for i in range(num)]
        return lst
    
    def getInfo(self):
        return self.info

class JobSchedule:
    DATABASE_NAME = "scheduler_log"
    EXECUTOR_MAX_NUM = 10
    INSTANCE_MAX_NUM = 5
    def __init__(self, username, passwd, host="localhost"):
        self.username = username
        self.passwd = passwd
        self.host = host
        self.dbms = DBMS(self.username, self.passwd,self.host)
        self.jobHandler = {}
        jobstores = {"default":SQLAlchemyJobStore(url="mariadb://{}:{}@{}/{}".format(username, passwd, host, JobSchedule.DATABASE_NAME))}
        executors = {"default":ThreadPoolExecutor(JobSchedule.EXECUTOR_MAX_NUM)}
        job_defaults= {"coalesce":True, "max_instances":JobSchedule.INSTANCE_MAX_NUM}
        self.sched = BlockingScheduler(jobstores=jobstores, executors=executors, job_defaults=job_defaults, timezone=str(tzlocal.get_localzone()))
        
    def __initJobs__(self):
        self.jobHandler[ZHIHUHOT.TAG] = ZHIHUHOT()
        self.jobHandler[EMBED.TAG] = EMBED()
        self.jobHandler[QUOTE.TAG] = QUOTE()
        self.sched.add_job(JobSchedule.fetchZhihuHotDataEveryDay, "cron", id=ZHIHUHOT.TAG, replace_existing=True, day_of_week = "0-6", hour="*/6")
        self.sched.add_job(JobSchedule.embeddingFilesEveryDay, "cron", id=EMBED.TAG, replace_existing=True, day_of_week = "1,3,5", hour="*/12")
        self.sched.add_job(JobSchedule.fetchQuoteEveryDay, "cron", id=QUOTE.TAG, replace_existing=True, day_of_week = "0-4", hour="9-12,13-15", minute="*/10")

    def __initDatabases__(self, passwd):
        oper = self.dbms.connect(user="root", passwd=passwd, database="")
        self.dbms.createDatabase(oper, JobSchedule.DATABASE_NAME)
        self.dbms.newUser(oper, self.username, self.passwd, JobSchedule.DATABASE_NAME)
        for tag in self.jobHandler.keys():
            self.dbms.createDatabase(oper, self.jobHandler[tag].DATABASE_NAME)
            self.dbms.newUser(oper, self.username, self.passwd, self.jobHandler[tag].DATABASE_NAME)
    
    def embeddingFilesEveryDay():
        dbms = DBMS.getInstance()
        embed = EMBED.getInstance()
        oper = dbms.connect(database=EMBED.DATABASE_NAME)
        dbms.createTables(oper, embed.getTables())
        embed.exec(oper)
        
    def fetchZhihuHotDataEveryDay():
        zhihu = ZHIHUHOT.getInstance()
        dbms = DBMS.getInstance()
        oper = dbms.connect(database=ZHIHUHOT.DATABASE_NAME)
        dbms.createTables(oper, zhihu.getTables())
        zhihu.fetchHot(oper)

    def fetchQuoteEveryDay():
        quote = QUOTE.getInstance()
        dbms = DBMS.getInstance()
        oper = dbms.connect(database=QUOTE.DATABASE_NAME)
        dbms.createTables(oper, quote.getTables())
        quote.fetch(oper)

    def start(self, rootpwd):
        self.__initJobs__()
        self.__initDatabases__(rootpwd)
        JobSchedule.fetchZhihuHotDataEveryDay()
        JobSchedule.fetchQuoteEveryDay()
        Log.i("Job Scheduler running")
        self.sched.start()
    
    def getData(self, tag, num, year, month, day):
        ret = None
        if(tag in self.jobHandler.keys()):ret = self.jobHandler[tag].getData(num, year=year, month=month, day=day)
        return ret
